#include "Terrain.h"



Terrain::Terrain (const WCHAR* file_name) : _altitude (MAX_ALTITUDE)
{
  if (file_name == NULL)
  {
    _width = _height = 0;
    return;
  }

  load_terrain (file_name);
  calc_and_smooth_normal ();
  handle_surface_info ();
}

void Terrain::load_terrain (const WCHAR* file_name)
{

  ULONG_PTR gdipToken;
  GdiplusStartupInput gdipSIn;

  GdiplusStartup (&gdipToken, &gdipSIn, NULL);

  // Load bitmap
  Ptr< Bitmap > pBitmap = new Bitmap (file_name);

  // Get data of the bitmap
  BitmapData bitmapData;
  Rect wholeImageArea (0, 0, pBitmap->GetWidth (), pBitmap->GetHeight ());

  pBitmap->LockBits (&wholeImageArea, ImageLockModeWrite,
                   PixelFormat24bppRGB, &bitmapData);

  // Generate vertexes from bitmap data
  read_data (bitmapData);

  pBitmap->UnlockBits (&bitmapData);

  // release bitmap
  pBitmap = NULL;

  GdiplusShutdown (gdipToken);

}


void Terrain::read_data (BitmapData& bitmapData)
{

  _height = bitmapData.Height - 1;
  _width = bitmapData.Width - 1;
  _vertexs.resize (_height + 1, _width + 1);

  BYTE* bitmapInByte = (BYTE*)bitmapData.Scan0;
  
  for (uint y = 0; y < bitmapData.Height; ++y)
  {
    for (uint x = 0; x < bitmapData.Width; ++x)
    {
      int z = bitmapInByte[y * bitmapData.Stride + x * 3 + 1];
      _vertexs[y][x].set (x, y, (float)z * _altitude / 255.0f);
      _vertexs[y][x].set_color (0.2f, 1.0f, 0.2f);
    }
  }
}


void Terrain::handle_surface_info ()
{
  _blocks.resize (_height, _width * 2);
  for (uint y = 0; y < _blocks.row_num (); ++y)
    for (uint x = 0; x < _blocks.column_num (); ++x)
    {
      _blocks[y][x].set_pos (x, y);

      if (x % 2 == 0) // Left part
      {
        _blocks[y][x].set_vertex (&(_vertexs[y][x / 2]),
                                  &(_vertexs[y + 1][x / 2]),
                                  &(_vertexs[y][x / 2 + 1]));
      }
      else // Right part
      {
        _blocks[y][x].set_vertex (&(_vertexs[y][x / 2 + 1]),
                                  &(_vertexs[y + 1][x / 2]),
                                  &(_vertexs[y + 1][x / 2 + 1]));
      }
    }
}


void Terrain::calc_and_smooth_normal ()
{
  for (uint y = 0; y < _vertexs.row_num (); ++y)
    for (uint x = 0; x < _vertexs.column_num (); ++x)
      _vertexs[y][x].change_normal (_vertexs);

  for (uint y = 0; y < _vertexs.row_num (); ++y)
    for (uint x = 0; x < _vertexs.column_num (); ++x)
      _vertexs[y][x].smooth_normal (_vertexs);
}


float Terrain::altitude (float x, float y) const
{
  pair< int, int > blockIndex = _get_block (x, y);
  if (blockIndex.first < 0 || blockIndex.second < 0)
    return -1;

  float dk = _altitude / 50;

  return _blocks[blockIndex.second][blockIndex.first].altitude (x, y) + dk;
}


Normal3f Terrain::normal (float x, float y) const
{
  pair< int, int > blockIndex = _get_block (x, y);
  if (blockIndex.first < 0 || blockIndex.second < 0)
    return Normal3f (-1, -1, -1);

  return _blocks[blockIndex.second][blockIndex.first].normal ();
}


pair< int, int > Terrain::_get_block (float x, float y) const
{
  pair< int, int > ret;
  if (x >= _width || y >= _height)
    return pair< int, int > (-1, -1);

  ret.second = (int) y;

  if (x - float(int(x)) + y - float(int(y)) < 1)
    ret.first = 2 * int(x);
  else
    ret.first = 2 * int(x) + 1;

  return ret;
}